%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "symbol.h"
#include "mixalSymbol.h"
#include "y.tab.h"

int yywrap(void) {
	// I can not understand why the action about regular pattern '\n' would be
	// triggerd when scanning the end of file.
	// Therefore, I have to decrement the 'yylineno' here.
	--yylineno;
	return 1;
}

void adjustCode(char ch) {
	// 'ch' is [AX1-6]
	if(ch < 'A')
		yylval.op.code += ch - '0'; // [1-6]
	else if(ch > 'A')
		yylval.op.code += 7; // X
}

char* copy(char* src) {
	char* ret = (char*)malloc(strlen(src) + 1);
	strcpy(ret, src);
	return ret;
}

extern int isOp;

%}

%x COMMENT
%x ALFALF

%%
# {BEGIN(COMMENT); continue;}
<COMMENT>\n {++yylineno; BEGIN(INITIAL); continue;}
<COMMENT>. {continue;}
\n {
	++yylineno;
	if(isOp) {
		isOp = 0;
		return '\n';
	}
	continue;
}
\t|" " {continue;}
, {return COMMA;}
"(" {return LPAREN;}
")" {return RPAREN;}
: {return COLON;}
= {return EQ;}
[0-9]+ {yylval.num = atoi(yytext); return NUM;}
[0-9](H|B|F) {
	yylval.sym.kind = Local;
	int digit = yytext[0] - '0';
	if(yytext[1] == 'B')
		digit -= 10;
	else if(yytext[1] == 'F')
		digit += 10;
	yylval.sym.local = digit;
	return SYMBOL;
}
"+" {return PLUS;}
"-" {return MINUS;}
"*" {return STAR;}
"//" {return SEDIV;}
"/" {return DIV;}
ORIG {return ORIG;}
EQU {return EQU;}
CON {return CON;}
ALF {BEGIN(ALFALF); return ALF;}
END {return END;}
LD[AX1-6]N? {
	yylval.op.field = 5;
	yylval.op.code = 8;
	adjustCode(yytext[2]);
	if(yyleng == 4)
		yylval.op.code += 8;
	return OP;
}
ST[AJXZ1-6] {
	yylval.op.field = 5;
	if(yytext[2] == 'J') {
		yylval.op.field = 2;
		yylval.op.code = 32;
	}
	else if(yytext[2] == 'Z') 
		yylval.op.code = 33;
	else {
		yylval.op.code = 24;
		adjustCode(yytext[2]);
	}
	return OP;
}
(ENT|ENN|INC|DEC)[AX1-6] {
	if(yytext[0] == 'I') 
		yylval.op.field = 0;
	else if(yytext[0] == 'D')
		yylval.op.field = 1;
	else if(yytext[2] == 'T')
		yylval.op.field = 2;
	else
		yylval.op.field = 3;
	yylval.op.code = 48;
	adjustCode(yytext[3]);
	return OP;
}
CMP[AX1-6] {
	yylval.op.field = 5;
	yylval.op.code = 56;
	adjustCode(yytext[3]);
	return OP;
}
JMP|JSJ|JOV|JNOV {
	yylval.op.code = 39;
	if(yytext[1] == 'M')
		yylval.op.field = 0;
	else if(yytext[1] == 'S')
		yylval.op.field = 1;
	else if(yytext[1] == 'O')
		yylval.op.field = 2;
	else
		yylval.op.field = 3;
	return OP;
}
J(L|G)E? {
	yylval.op.code = 39;
	yylval.op.field = yytext[1] == 'L' ? 4 : 6;
	if(yyleng == 3)
		yylval.op.field = 13 - yylval.op.field;
	return OP;
}
JN?E {
	yylval.op.code = 39;
	yylval.op.field = 5;
	if(yytext[1] == 'N')
		yylval.op.field = 8;
	return OP;
}
J[AX1-6](N|Z|P|NN|NZ|NP) {
	switch(yytext[yyleng - 1]) {
		case 'N': yylval.op.field = 0; break;
		case 'Z': yylval.op.field = 1; break;
		case 'P': yylval.op.field = 2; break;
	}
	if(yyleng == 4)
		yylval.op.field += 3;
	yylval.op.code = 40;
	adjustCode(yytext[1]);
	return OP;
}
ADD|SUB|MUL|DIV {
	switch(yytext[0]) {
		case 'A':	yylval.op.code = 1; break;
		case 'S':	yylval.op.code = 2; break;
		case 'M':	yylval.op.code = 3; break;
		case 'D':	yylval.op.code = 4; break;
	}
	yylval.op.field = 5;
	return OP;
}
SLA|SRA|SLAX|SRAX|SLC|SRC {
	switch(yytext[yyleng - 1]) {
		case 'A': yylval.op.field = 0; break;
		case 'X': yylval.op.field = 2; break;
		case 'C': yylval.op.field = 4; break;
	}
	if(yytext[1] == 'R')
		++yylval.op.field;
	yylval.op.code = 6;
	return OP;
}
MOVE {
	yylval.op.code = 7;
	yylval.op.field = 1;
	return OP;
}
NOP {
	yylval.op.code = 0;
	return OP;
}
HLT {
	yylval.op.code = 5;
	yylval.op.field = 2;
	return OP;
}
IN {
	yylval.op.code = 36;
	return OP;
}
OUT {
	yylval.op.code = 37;
	return OP;
}
IOC {
	yylval.op.code = 35;
	return OP;
}
JRED {
	yylval.op.code = 38;
	return OP;
}
JBUS {
	yylval.op.code = 34;
	return OP;
}
NUM|CHAR {
	yylval.op.code = 5;
	yylval.op.field = yyleng == 4;
	return OP;
}
([A-Za-z]|[0-9])*[A-Za-z]([A-Za-z]|[0-9])* {
	yylval.sym.kind = Global;
	yylval.sym.global = S_symbol(copy(yytext));
	return SYMBOL;
}
<ALFALF>[!-Z][ -Z]* {
	yylval.str = copy(yytext);
	BEGIN(INITIAL);
	return STRING;
}
<ALFALF>\t|" " {continue;}
<ALFALF>\n {++yylineno; continue;} 
<ALFALF>. {printf("illegal token\n"); continue;}
. {
	printf("illegal token\n");
	continue;
}
